package cn.yunyichina.provider.hisiface.http;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.multipart.Attribute;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType;
import io.netty.util.CharsetUtil;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.yunyichina.provider.framework.data.SpringContextHandler;
import cn.yunyichina.provider.hisiface.invoke.impl.OutsideInvokeServiceImpl;
import cn.yunyichina.provider.iface.entity.common.Response;
import cn.yunyichina.utils.convert.JsonUtils;

public class HttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {

	 private static final Logger logger = LoggerFactory.getLogger(HttpServerHandler.class);

    /* 此变量用来存储最终返回给用户的数据 */
    private String responseContent = "";

    /* 是否需要关闭当前的http请求 */
    private boolean closeHttp = false;

    /* 解密post报文要用到的 */
    private static final HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); // Disk

    private HttpPostRequestDecoder decoder;
    
    private OutsideInvokeServiceImpl outsideInvokeService = SpringContextHandler.getBean("outsideInvokeServiceImpl");

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        if (decoder != null) {
            decoder.cleanFiles();
        }
        ctx.flush();
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        HttpRequest request = (HttpRequest) msg;

        // 1.判断解码是否失败
        if (request.decoderResult().isFailure()) {
            responseContent = JsonUtils.toJsonString(new Response("4101", "解码失败"));
            closeHttp = true;
            writeResponse(request, ctx);
            return;
        }

        // 2.打印请求报文
        URI uri = new URI(request.uri());
        if (uri.getPath().equals("/favicon.ico")) {
            return;
        }
        printRequest(request);

        // 3.获取请求传入的参数
        Map<String, String> httpContent = new HashMap<>();
        if (uri.getPath().equals("/openservice")) {
        	if (request.method().equals(HttpMethod.POST)) {
        		decoder = new HttpPostRequestDecoder(factory, request);
                try {
                    while (decoder.hasNext()) {
                        // 解释http post数据
                        InterfaceHttpData data = decoder.next();
                        if (data != null) {
                            try {
                                if (data.getHttpDataType() == HttpDataType.Attribute) {
                                    Attribute attribute = (Attribute) data;
                                    String key = attribute.getName();
                                    String value = null;
                                    try {
                                        value = attribute.getValue();
                                    } catch (IOException e1) {
                                        logger.error("Body attribute: " + attribute.getHttpDataType().name() + ":"
                                                + key + " error while reading value" + e1);
                                    }
                                    httpContent.put(key, value);
                                }
                            } finally {
                                data.release();
                            }
                        }
                    }
                } catch (EndOfDataDecoderException e1) {
                	// 解码结束
                } catch (Exception e) {
                	logger.error("解释Http POST协议出错", e);
                    closeHttp = true;
                    responseContent = JsonUtils.toJsonString(new Response("4103", "解释Http POST协议出错: " + e.toString()));
                    writeResponse(request, ctx);
                    return;
                }
        	} else {
				closeHttp = true;
				responseContent = JsonUtils.toJsonString(new Response("4104", "对外接口只支持Http/Https POST协议"));
				writeResponse(request, ctx);
				return;
        	}
        } else {
        	responseContent = JsonUtils.toJsonString(new Response("4102", "请求路径错误"));
            closeHttp = true;
            writeResponse(request, ctx);
            return;
        }
        
        // 4.交给业务层处理
        responseContent = outsideInvokeService.openService(httpContent);

        // 5.写回给客户端
        writeResponse(request, ctx);
    }

    /**
     * 写入Response返回
     *
     * @param request
     * @param ctx
     * @throws URISyntaxException
     */
    private void writeResponse(HttpRequest request, ChannelHandlerContext ctx) throws URISyntaxException {

    	// 1. 当不是主动关闭http通道的时候检测
		if (!closeHttp) {
            if (request.headers().contains(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE, true)) {
                closeHttp = true;
            } else if (request.protocolVersion().equals(HttpVersion.HTTP_1_0)) {
                closeHttp = true;
            }
        }

        // 2. 当closeHttp为false的时候
        boolean keepAlive = false;
        if (!closeHttp) {
            keepAlive = HttpUtil.isKeepAlive(request);
        }

        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, request.decoderResult()
                .isSuccess() ? HttpResponseStatus.OK : HttpResponseStatus.BAD_REQUEST, Unpooled.copiedBuffer(
                responseContent, CharsetUtil.UTF_8));
        responseContent = "";
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=UTF-8");

        if (keepAlive && HttpServerConfig.getConfig().isKeepAlive()) {
            response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
            response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        }

        // 3. 是否支持http的keep-alive
        if (!HttpServerConfig.getConfig().isKeepAlive()) {
            closeHttp = true;
        }

        ChannelFuture future = ctx.writeAndFlush(response);
        if (closeHttp) {
            future.addListener(ChannelFutureListener.CLOSE);
            logger.info("服务器主动关闭远程链接");
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        logger.warn("Requst is exception", cause);
        ctx.close();
    }

    /**
     * 打印请求信息
     *
     * @param request
     */
    private void printRequest(HttpRequest request) {
        StringBuilder builder = new StringBuilder();
        builder.append("\r\n");
        builder.append("####################### BEGIN PRINT REQUEST #######################").append("\r\n");
        builder.append("HOST: ").append(request.headers().get(HttpHeaderNames.HOST, "unknown")).append("\r\n");
        builder.append("PROTOCOL_VERSION: ").append(request.protocolVersion()).append("\r\n");
        builder.append("REQUEST_METHOD: ").append(request.method()).append("\r\n");
        builder.append("REQUEST_URI: ").append(request.uri()).append("\r\n");

        HttpHeaders headers = request.headers();
        if ((headers != null) && !headers.isEmpty()) {
            for (Map.Entry<String, String> header : headers) {
                builder.append("HEADER: ").append(header.getKey()).append(" = ").append(header.getValue()).append("\r\n");
            }
        }
        
        builder.append("####################### END PRINT REQUEST #######################");
        
        logger.info(builder.toString());
    }
}
